package com.sunchp.nio.sample.thrift;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

public class NonblockingTransport implements Transport {
    private static final Logger LOGGER = LoggerFactory.getLogger(NonblockingTransport.class);

    private SocketAddress socketAddress;
    private SocketChannel socketChannel;

    public NonblockingTransport(String host, int port, int timeout) throws IOException {
        this(SocketChannel.open(), timeout, new InetSocketAddress(host, port));
    }

    public NonblockingTransport(SocketChannel socketChannel) throws IOException {
        this(socketChannel, 0, null);
        if (!socketChannel.isConnected()) throw new IOException("Socket must already be connected");
    }

    public NonblockingTransport(SocketChannel socketChannel, int timeout, SocketAddress socketAddress) throws IOException {
        this.socketChannel = socketChannel;
        this.socketAddress = socketAddress;

        // make it a nonblocking channel
        socketChannel.configureBlocking(false);

        // set options
        Socket socket = socketChannel.socket();
        socket.setSoLinger(false, 0);
        socket.setTcpNoDelay(true);
        socket.setKeepAlive(true);
        socket.setSoTimeout(timeout);
    }

    public boolean startConnect() throws IOException {
        return socketChannel.connect(socketAddress);
    }

    public boolean finishConnect() throws IOException {
        return socketChannel.finishConnect();
    }

    public SelectionKey registerSelector(Selector selector, int interests) throws IOException {
        return socketChannel.register(selector, interests);
    }

    public int read(ByteBuffer buffer) throws IOException {
        return socketChannel.read(buffer);
    }

    public int write(ByteBuffer buffer) throws IOException {
        return socketChannel.write(buffer);
    }

    @Override
    public void close() {
        try {
            socketChannel.close();
        } catch (IOException iox) {
            LOGGER.warn("Could not close socket.", iox);
        }
    }
}
